package com.db4o.odbgen.plugins.java.maingenerator.typedxmlschema;

import java.util.ArrayList;
import java.util.List;


import com.db4o.internal.odbgen.StringUtils;
import com.db4o.odbgen.OdbgenException;
import com.db4o.odbgen.plugins.xmlschema.Element;


public class TSClass implements XmlElement, TypeDeclaration{
	
	private List<String> _imports;
	private List<TSModifier> _modifiers;
	private List<TSMethod> _methods;
	private String _package;
	private String _simpleName;
	private TSClass _superClass;
	private List<TSInterface> _interfaces;
	private List<TSField> _fields;
	private StringBuilder _additionalContent;
	private Element _element;

	
	
	/**
	 * Constructs class given it's fields.
	 * @param element The element within XmlSchema.
	 * This may be null if the class is outside XmlSchema. 
	 * @param packageName The name of the package. This must not be null even for default packages.
	 * @param modifiers The list of modifiers for tha class. This must not be null. It may be an empty list.
	 * @param simpleName The name of the class.
	 * @param fields The list of fields. This must not be null. It may be an empty list.
	 * @param superClass The super class. This is null if there is no superclass.
	 * @param interfaces The list of interfaces. This must not be null. It may be an empty list.
	 * @throws OdbgenException
	 */
	public TSClass(
			Element element,
			String packageName,
			List<TSModifier> modifiers,
			String simpleName, 
			List<TSField> fields, 
			TSClass superClass, 
			List<TSInterface> interfaces
			) throws OdbgenException{
		
		//Sanity checks.
		if(packageName==null){
			throw new OdbgenException("Package name cannot be null.");
		}
		if(modifiers==null){
			throw new OdbgenException("Class modifiers must be specified.");
		}
		if(simpleName==null){
			throw new OdbgenException("Simple name cannot be null.");
		}
		if(fields==null){
			throw new OdbgenException("Fields must be specified.");
		}
		if(interfaces==null){
			throw new OdbgenException("Interfaces must be specified.");
		}
		
		this._element = element;
		this._package = packageName;
		this._modifiers = modifiers;
		this._simpleName = simpleName;
		this._fields = fields;
		this._superClass = superClass;
		this._interfaces = interfaces;
		
		this._imports = new ArrayList<String>();
		this._methods = new ArrayList<TSMethod>();
		this._additionalContent = new StringBuilder();
	}

	/**
	 * See {@link XmlElement} for details.
	 */
	@Override
	public Element getElement() {
		return this._element;
	}
	
	/**
	 * See {@link TypeDeclaration} for more details.
	 */
	@Override
	public String getPackage() {
		return this._package;
	}

	/**
	 * See {@link TypeDeclaration} for more details.
	 */
	@Override
	public String getQualifiedName() {
		return this._package+(this._package.length()==0?"":".")+this._simpleName;
	}

	/**
	 * See {@link TypeDeclaration} for more details.
	 */
	@Override
	public String getSimpleName() {
		return this._simpleName;
	}
	
	/**
	 * Returns the direct superclass of this type element.
	 * Returns null if this class does not have a superclass (ie the superclass is Object).
	 */
	public TSClass getSuperClass(){
		return this._superClass;
	}
	void setSuperClass(TSClass superClass){
		this._superClass = superClass;
	}
	
	/**
	 * Returns the interface types directly implemented by this class.
	 * This is never null.
	 */
	public List<TSInterface> getInterfaces(){
		if(this._interfaces==null){
			this._interfaces = new ArrayList<TSInterface>();
		}
		return this._interfaces;
	}
	void setInterfaces(List<TSInterface> interfaces){
		this._interfaces = interfaces;
	}
	
	public List<TSField> getFields(){
		return this._fields;
	}
	
	/**
	 * Returns the list of imports needed for this class.
	 */
	public List<String> getImports(){
		return this._imports;
	}

	/**
	 * Returns the modifiers of this class.
	 * This may be used to add more modifiers.
	 */
	public List<TSModifier> getModifiers(){
		return this._modifiers;
	}
	
	/**
	 * Returns the list of methods for this class.
	 * This may be used to add more methods.
	 */
	public List<TSMethod> getMethods(){
		return this._methods;
	}

	/**
	 * Returns a method given its name.
	 * @param methodName The name of the method to search for.
	 * @return The requested method if found. Otherwise returns null.
	 */
	public TSMethod getMethodByName(String methodName) {
		for(TSMethod method : this.getMethods()){
			if(methodName.equals(method.getName())){
				return method;
			}
		}
		return null;
	}
	
	
	/**
	 * Returns a string representing this object.
	 * This is used for debug purposes.
	 * @param tabs Number of tabs to indent.
	 */
	String toString(int tabs) {
		
		StringBuilder res = new StringBuilder();
		
		res.append(String.format("%sclass", StringUtils.tabs(tabs)));
		res.append(StringUtils.newLine());

		res.append(String.format("%sname=%s",
				StringUtils.tabs(tabs+1),
				this.getQualifiedName()
				));
		res.append(StringUtils.newLine());

		if(this.getSuperClass()!=null){
			res.append(String.format("%ssuperclass=%s",
					StringUtils.tabs(tabs+1),
					this.getSuperClass().getQualifiedName()
			));
			res.append(StringUtils.newLine());
		}
		
		StringBuilder interfaces = new StringBuilder();
		for(TSInterface i : this.getInterfaces()){
			interfaces.append(i.getQualifiedName());
			interfaces.append(", ");
		}
		
		if(this.getInterfaces().size()>0){
			res.append(String.format("%sinterfaces=%s",
					StringUtils.tabs(tabs+1),
					interfaces.toString()
			));
			res.append(StringUtils.newLine());
		}

		
		for(TSField f : this.getFields()){
			res.append(f.toString(tabs+1));
			res.append(StringUtils.newLine());
		}
		
		return res.toString();
	}

	/**
	 * Returns the additional content for this class.
	 * The returned value may be used in order to add andditional content.
	 * The additional content is used to add arbitrary code to the class.
	 * This code cannot be retrieved/inspected/modified by using methods as getFields() or
	 * getMethods().
	 * This is just code that will be written to the source file as is.
	 * @return
	 */
	public StringBuilder getAdditionalContent() {
		return this._additionalContent;
	}

	
}



